package com.brooks.demo.program.p2;

import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 写一个固定容量同步容器，拥有put和get方法，以及getCount方法，能够支持2个生产者线程以及10个消费者线程的阻塞调用
 *
 * 这里使用了reentrantLock ,利用了等待队列，有效的解决了demo1的问题，把生产者和消费者分别放到两个等待队列，叫醒的
 * 时候就只需要叫醒相应的等待队列就可以了
 * @Author mihutu
 * @Date 2020/10/21 9:46
 * @Version 1.0
 */
public class Demo2<T> {
    final private LinkedList<T> list = new LinkedList<T>();
    /**
     * 最多10个元素
     */
    final private int MAX = 10;
    private int count;

    private Lock lock = new ReentrantLock();
    private Condition products = lock.newCondition();
    private Condition customs = lock.newCondition();

    public void put(T t) {
        //这里不使用if 是因为我想线程被唤醒的时候再去判断一下元素是否是满的
        lock.lock();
        try {
            while (list.size() == MAX) {
                //容器满了，阻塞，不能添加元素了
                products.await();
            }
            list.add(t);
            ++count;
            customs.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public T get() {
        T t = null;
        lock.lock();
        try {
            //这里原因同上
            while (list.size() == 0) {
                customs.await();
            }
            t = list.removeFirst();
            count--;
            products.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return t;
    }

    public static void main(String[] args) {
        Demo2<String> d = new Demo2<>();
        //两个生产者线程
        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                //每个生产25个
                for (int j = 0; j < 25; j++) {
                    d.put(Thread.currentThread().getName() + " " + j);
                }
            }, "p" + i).start();
        }
        //10个消费者线程
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                //每个消费五个
                for (int j = 0; j < 5; j++) {
                    System.out.println(d.get());
                }
            }, "c" + i).start();
        }
    }
}
